package be.ipl.finito.domaine;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Random;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.MapKeyColumn;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.persistence.Transient;
import javax.persistence.UniqueConstraint;
import javax.swing.Timer;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;

import be.ipl.finito.util.Util;

import com.sun.istack.internal.NotNull;

@SuppressWarnings("serial")
@Entity
@Table(name="PARTIES", schema="FINITO")
public class Partie implements Serializable {
	
	
	public final static int NBR_MIN_JOUEURS = 2;
	public final static int NBR_MAX_JOUEURS = 3;
	//Temps en seconde
	public final static int TEMPS = 30;
	
	public enum Etat {
		
		EN_ATTENTE {
			@Override
			public boolean ajouterPlateau(Partie p, Plateau pl) {
				if(p.getPlateaux().contains(pl))
					return false;
				if(p.getJoueurs().isEmpty()){
					return false;
				}
				if(p.getJoueurs().size() >= NBR_MAX_JOUEURS)
					return false;
				return p.plateaux.add(pl);
			}
			
			@Override
			public boolean demarrerPartie(Partie partie) {
				if(partie.getJoueurs().size()<=1){
					partie.etat = Etat.SUPPRIMER;
				}
				partie.etat = Etat.EN_COURS;
				return true;
			}
			
			@Override
			public boolean supprimerPartie(Partie partie) {
				partie.etat = SUPPRIMER;
				return true;
			}
			
			
		}, 
		EN_COURS {
			@Override
			public Jeton prochainJeton(Partie partie) {
				
				/*for (Plateau p : partie.plateaux) {
					if(p.calculerScore() == 12){
						partie.etat = Etat.TERMINE;
						return null;
					}
				}*/
				
				if(partie.indiceDernierJetonPlace == 12){
					partie.etat = Etat.EN_COURS2;
					return null;
				}
				return partie.donnerJeton();
			}
			
			@Override
			public Case placerJeton(Partie p, Joueur j, Jeton jeton, int place) {
				Case c = new Case(place);
				c.setJeton(jeton);
				for (Plateau pl : p.plateaux) {
					if(pl.getJoueur().equals(j)){
						pl.SupprimerJeton(c);
						pl.ajouterJeton(c);
						return c;
					}
				}
				return null;
			}
			
			@Override
			public boolean demarrerPartie(Partie partie) {
				return false;
			}
			
			@Override
			public boolean toutLeMondeAJoue(Partie partie) {
				Iterator<Plateau> it = partie.getPlateaux().iterator();
				while(it.hasNext()){
					Plateau p = it.next();
					if(p.getJetonsRetournes().size() < 3)
						return false;
				}
				return true;
			}
			
			@Override
			public boolean deconnecterJoueur(Partie partie, Joueur j) {
				Iterator<Plateau> it = partie.plateaux.iterator();
				while (it.hasNext()) {
					Plateau p = it.next();
					if (p.getJoueur().equals(j)) {
						partie.plateaux.remove(p);
						
						if (partie.plateaux.size() < 2)
							partie.etat = TERMINE;
						return true;
					}
				}
				return false;
			}
			
			@Override
			public int lancerDe(Partie partie) {
				return partie.getDe();
			}
			
		}, 
		
		EN_COURS2 {
			
			@Override
			public boolean deconnecterJoueur(Partie partie, Joueur j) {
				Iterator<Plateau> it = partie.plateaux.iterator();
				while (it.hasNext()) {
					Plateau p = it.next();
					if (p.getJoueur().equals(j)) {
						partie.plateaux.remove(p);
						
						if (partie.plateaux.size() < 2)
							partie.etat = TERMINE;
						return true;
					}
				}
				return false;
			}
			
			@Override
			public boolean demarrerPartie(Partie partie) {
				return false;
			}
			
			@Override
			public int lancerDe(Partie partie) {
				for (Plateau p : partie.plateaux) {
					if(p.calculerScore() == 12){
						partie.etat = Etat.TERMINE;
						return 0;
					}
				}
				return partie.getDe();
			}
			
			@Override
			public boolean toutLeMondeAJoue(Partie partie) {
				// TODO Auto-generated method stub
				return super.toutLeMondeAJoue(partie);
			}
			
		},
		TERMINE {
			
		}, 
		SUPPRIMER {
			public boolean supprimerPartie(Partie partie) {
				return true;
			}
			
			
		},
		EN_PAUSE {
			@Override
			public boolean demarrerPartie(Partie partie) {
				if (partie.plateaux.size() < 1) {
					partie.etat = Etat.SUPPRIMER;
					return false;
				}
				partie.etat = Etat.EN_COURS;
				return true;
			}
		};
		
		public boolean deconnecterJoueur(Partie partie, Joueur j){
			return false;
		}
		
		public boolean ajouterJoueur(Partie partie, Joueur joueur) {
			return false;
		}
		
		public boolean demarrerPartie(Partie partie) {
			throw new UnsupportedOperationException();
		}
		
		public boolean supprimerPartie(Partie partie) {
			return false;
		}
		
		public boolean mettreEnPausePartie(Partie partie) {
			return false;
		}
		
		public boolean terminerPartie(Partie partie) {
			return false;
		}
		
		public Jeton prochainJeton(Partie partie) {
			throw new UnsupportedOperationException();
		}
		
		public boolean placerJeton(Partie partie, Jeton jeton) {
			return false;
		}
		
		public boolean toutLeMondeAJoue(Partie partie){
			return false;
		}
		
		public boolean relancerPartie(Partie partie) {
			return false;
		}
		
		public boolean ajouterPlateau(Partie p, Plateau pl){
			return false;
		}
		
		public Jeton tourSuivant(Partie p){
			throw new UnsupportedOperationException();
		}

		public int lancerDe(Partie partie) {
			throw new UnsupportedOperationException();
		}
		
		public Case placerJeton(Partie p,Joueur j, Jeton jeton, int place) {
			throw new UnsupportedOperationException();
		}
	}
	
	
	
	@Id
	@GeneratedValue(strategy=GenerationType.IDENTITY)
	private int id;
	
	@NotNull
	@Column(unique=true)
	private String nom;
	
	@NotNull
	@Enumerated(EnumType.STRING)
	private Etat etat;
	
	@Min(1)
	@Max(20)
	private int de;
	
	@NotNull
	@Min(0)
	@Max(36)
	private int indiceDernierJetonPlace;
	
	@OneToMany
	@JoinColumn(name="PARTIE_ID")
	//@OneToMany(mappedBy = "partie", fetch = FetchType.EAGER)
	//?poser la question lequel est adequat
	private List<Plateau> plateaux = new ArrayList<Plateau>(); // TODO corriger +  lier avec bon schemas
	
	
	@Transient
	private List<Jeton> listeJeton;
	
	@Transient
	private int nbJoueurs;
	
	@Transient
	private int tourDuJeu;
	
	@Transient
	private Random random = new Random();
	
	
	
	@ManyToMany
	 @JoinTable(name = "TIRAGE", schema = "FINITO", joinColumns = { @JoinColumn(name = "PARTIE_ID") }, inverseJoinColumns = { @JoinColumn(name = "JETON_ID") }, uniqueConstraints = { @UniqueConstraint(columnNames = {
	   "PARTIE_ID", "JETON_ID" }) })
	 @MapKeyColumn(name = "ORDRE_TIRAGE")
	 private Map<Integer, Jeton> pioche = new HashMap<Integer, Jeton>();
	
	
	public int getNbJoueurs(){
		return this.plateaux.size();
	}
	
	public List<Joueur> getJoueurs(){
		List<Joueur> j = new ArrayList<Joueur>();
		for (Plateau p : this.plateaux) {
			j.add(p.getJoueur());
		}
		return j;
	}
	
	public List<Jeton> getListeJeton(){
		return this.listeJeton;
	}
	
	public List<Plateau> getPlateaux() {
		return plateaux;
	}

	public void setPlateaux(List<Plateau> plateaux) {
		this.plateaux = plateaux;
	}

	
	protected Partie(){
		
	}
	
	
	public Partie(String nom) {
		Util.checkString(nom);
		this.nom = nom;
		this.etat = Etat.EN_ATTENTE;
		this.tourDuJeu = 1;
		this.de = 1;
		
		// Initialisation de la liste des Jetons
		this.listeJeton = new ArrayList<Jeton>();
		for(int i=1; i<=12; i++) {
			this.listeJeton.add(new Jeton(i, -1));
		}
		Collections.shuffle(this.listeJeton);
		for(int i=1; i<=12; i++) {
			this.pioche.put(i, this.listeJeton.get(i-1));
		}
	}

	public Etat getEtat() {
		return etat;
	}

	public void setEtat(Etat etat) {
		this.etat = etat;
	}
	
	public int getTourDuJeu() {
		return this.tourDuJeu;
	}

	public int getIndiceDernierJetonPlace() {
		return indiceDernierJetonPlace;
	}

	public void setIndiceDernierJetonPlace(int indiceDernierJetonPlace) {
		this.indiceDernierJetonPlace = indiceDernierJetonPlace;
	}

	public int getId() {
		return id;
	}

	public int getDe(){
		de = random.nextInt((20 - 1) + 1) + 1;
		return this.de;
	}
	
	public String getNom() {
		return nom;
	}
	
	public int getNbrJoueur() {
		return plateaux.size();
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + id;
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Partie other = (Partie) obj;
		if (id != other.id)
			return false;
		return true;
	}
	
	private Jeton donnerJeton(){
		return this.pioche.get(this.tourDuJeu++);
	}

	
	public boolean supprimerPartie() {
		return this.etat.supprimerPartie(this);
	}
	
	public boolean ajouterPlateau(Plateau p){
		return this.etat.ajouterPlateau(this, p);
	}

	public boolean demarrerPartie() {
		return this.etat.demarrerPartie(this);
	}
	
	public Jeton prochainJeton() {
		return this.etat.prochainJeton(this);
	}
	
	
	public int lancerDe(){
		return this.etat.lancerDe(this);
	}

	public Case placerJeton(Joueur j, Jeton jeton, int place) {
		return this.etat.placerJeton(this, j, jeton, place);
	}
	
	public Map<Integer, Jeton> getPioches(){
		return this.pioche;
	}
	
	public boolean toutLeMondeAJoue(){
		return this.etat.toutLeMondeAJoue(this);
	}

}
